package hihocode;

import java.util.Scanner;


public class NumberOfIslandsII {

	//上下左右的四个方向
	private static final int[][] dir = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
	
	
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		NumberOfIslandsII object = new NumberOfIslandsII();
		int count = scanner.nextInt();
		char[][] grid = new char[1000][1000];
		int islandI, islandJ;
		int area = 0;
		int perimeter = 0;
		int islandCount = 0;
		UnionFind unionFind = object.new UnionFind(grid);
		for (int i = 0; i < count; i++) {
			islandI = scanner.nextInt();
			islandJ = scanner.nextInt();
			grid[islandI][islandJ] = '1';
//			int perimeter = object.islandPerimeter(grid);
			islandCount -= object.addCurIsland(islandI, islandJ, grid, unionFind);
			area++;
			perimeter += 4;
			perimeter -= object.neighbours(islandI, islandJ, grid) * 2;
			
			System.out.println(islandCount + " " + area + " " + perimeter);
		}
		scanner.close();
	}

	private class UnionFind {

		private int count = 0;
		public int[] id = null;

		int m = 1000, n = 1000;
		// 根据岛屿来完成初始化
		public UnionFind(char[][] grid) {
//			for (int i = 0; i < m; i++) {
//				for (int j = 0; j < n; j++) {
//					if (grid[i][j] == '1')
//						count++;
//				}
//			}
//			System.out.println("init count: " + count);
			id = new int[m * n];
			for (int i = 0; i < m * n; i++) {
				id[i] = i;
			}
		}

		public int count() {
			return count;
		}

		public boolean connected(int p, int q) {
			return find(p) == find(q);
		}

		// 我们将同一组的指向同一个元素
		// 得到下标为p的元素所指的元素
		public int find(int p) {
			while (p != id[p]) {
				id[p] = id[id[p]];
				p = id[p];
			}
			return p;
		}

		public void union(int p, int q) {
			// 获得p和q所指的元素
			int pID = find(p);
			int qID = find(q);
			// 如果两个元素相等，说明位于同一组，直接返回
			if (pID == qID) {
				return;
			}
			id[pID] = qID;
			count--;
		}
	}

	
	//思路不是这样的，这是扫描了一次整体的，我们应该仅仅扫描每次添加时候的上下左右，如果相连，则继续下一次
	//如果不相连，则判断是否为1
	//TLE, runTime : 22s
	public int numIslands(char[][] grid) {
		if (grid == null || grid[0] == null) {
			return 0;
		}
		int m = grid.length;
		int n = grid[0].length;
//		UnionFind unionFind = new UnionFind(m, n, grid);
		UnionFind unionFind = new UnionFind(grid);

		for (int i = 0; i < m; i++) {
			for (int j = 0; j < n; j++) {
				if (grid[i][j] != '1') {
					continue;
				}
				int p = i * n + j;
				if (j > 0 && grid[i][j - 1] == '1') {
					int q = p - 1;
					unionFind.union(p, q);
				}
				if (i > 0 && grid[i - 1][j] == '1') {
					int q = p - n;
					unionFind.union(p, q);

					// 无意之间发现一个我不知道的点，外部类竟然可以访问内部类的私有变量
					// System.out.println("i :" + i + " j: " + j +
					// "  count - 1 " + unionFind.count);
				}
			}
		}
		return unionFind.count();
	}
	
	
	//正确的思路如下
	//该方法返回的是该岛加入到了几个已有的岛中
	//AC  8228ms
	public int addCurIsland(int x, int y, char[][] grid, UnionFind uf) {
		int res = -1;
		int id = x * 1000 + y;
		for(int j = 0; j < 4; ++j){
			int pID = uf.find(id);
            int dx = x + dir[j][0], dy = y + dir[j][1];
            int curId = dx * 1000 + dy;
            if (dx < 0 || dx >= 1000 || dy < 0 || dy >= 1000) continue;
            if(grid[dx][dy] == '1'){
            	int qID = uf.find(curId);
            	if (qID != pID){
            		res++;
    			}
            	uf.id[pID] = qID;
            }
        }
		return res;
	}

	//获取与当前新添加的island相邻的个数
	public int neighbours(int x, int y, char[][] grid) {
		int res = 0;
		for(int j = 0; j < 4; ++j){
            int dx = x + dir[j][0], dy = y + dir[j][1];
            if (dx < 0 || dx >= 1000 || dy < 0 || dy >= 1000) continue;
            if(grid[dx][dy] == '1'){
            	res++;
            }
        }
		return res;
	}
	
	//此方法会遍历所有整个海域，没有有效的利用已知条件
	public int islandPerimeter(char[][] grid) {
		// 周长 = islands * 4 - neighbours * 2
		// 也就是说每多一个邻居，就少两条边
		int islands = 0, neighbours = 0;
		for (int i = 0; i < grid.length; i++) {
			for (int j = 0; j < grid[0].length; j++) {
				if (grid[i][j] == '1') {
					islands++;
					// 如果判断上下左右是否是1的时候，那么最后得到的neighbours一定会是双倍
					// 所以可以设定只判断下面和右面的
					if (i < grid.length - 1 && grid[i + 1][j] == '1') {
						neighbours++;
					}
					if (j < grid[0].length - 1 && grid[i][j + 1] == '1') {
						neighbours++;
					}
				}
			}
		}
		return islands * 4 - neighbours * 2;
	}
}
